package com.supos.progress.redmine.query;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.supos.progress.entity.CycleDate;
import com.supos.progress.entity.Output;
import com.supos.progress.entity.RIssus;
import com.supos.progress.entity.RProject;
import com.supos.progress.util.DateUtil;
import com.taskadapter.redmineapi.MembershipManager;
import com.taskadapter.redmineapi.RedmineException;
import com.taskadapter.redmineapi.RedmineManager;
import com.taskadapter.redmineapi.bean.CustomField;
import com.taskadapter.redmineapi.bean.Issue;
import com.taskadapter.redmineapi.bean.Membership;
import com.taskadapter.redmineapi.bean.Project;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class IssueQuery {
    @Autowired
    private RedmineManager manager;
    //    static List<RProject> rProjects = null;
    private static Set<String> moduleNames = new HashSet<>();
    private static final String PROJECT_START_DATE_SPRINT1 = "2018-01-31";
    private static Map<String, CycleDate> cycleDateMap = null;
    private static final String IDENTIFIER_PREFIX = "sprint";
    private static final String ALL_PROJECTS_DATA = "all_data";
    private static final String CYCLE_DATA_MAP = "cycleDateMap";


    final static Cache<String, List<RProject>> cache = CacheBuilder.newBuilder()
            //设置cache的初始大小为10，要合理设置该值
            .initialCapacity(10)
            //设置并发数为5，即同一时间最多只能有5个线程往cache执行写入操作
            .concurrencyLevel(5)
            //设置cache中的数据在写入之后的存活时间为10秒
            .expireAfterWrite(1, TimeUnit.HOURS)
            //构建cache实例
            .build();

    final static Cache<String, Map<String, CycleDate>> cacheCyc = CacheBuilder.newBuilder()
            //设置cache的初始大小为10，要合理设置该值
            .initialCapacity(10)
            //设置并发数为5，即同一时间最多只能有5个线程往cache执行写入操作
            .concurrencyLevel(5)
            //设置cache中的数据在写入之后的存活时间为10秒
            .expireAfterWrite(1, TimeUnit.HOURS)
            //构建cache实例
            .build();

    public void initData() throws RedmineException {
        cache.put(ALL_PROJECTS_DATA, allIssusData());
        cacheCyc.put(CYCLE_DATA_MAP, initCycleDate());
    }

    /**
     * 各版本 进度
     *
     * @param identifier
     * @return
     * @throws RedmineException
     */
    public List<Output> query(String identifier) throws RedmineException {
//        initData();
        RProject rp = queryByProjectName(identifier);
        if (rp == null)
            return null;
        Set<String> teamLeader = getTeamleader(identifier);
        List<Output> result = new ArrayList<>();
        Output output = null;
        for (String module : moduleNames) {
//            List<RIssus> rIssuses = rp.getRIssuses().stream().filter(rIssus -> {return module.equals(rIssus.getModule());}).collect(Collectors.toList());
            List<Output> outputsAuthorName = new ArrayList<>();
            for (String authorName : teamLeader) {
                output = new Output();
                output.setChild(rp.getRIssuses().stream().filter(rIssus -> {
                    return module.equals(rIssus.getModule()) && authorName.equals(rIssus.getAssigneeName()) && rIssus.getParentId() == null;
                })
                        .map(rIssus -> {
                            Output o = new Output();
                            o.setTitleOrName(rIssus.getSubject());
                            o.setRatio(rIssus.getAssigneeDoneRatio());
                            return o;
                        }).collect(Collectors.toList()));
                if (output.getChild() == null || output.getChild().isEmpty())
                    continue;
                output.setTitleOrName(authorName);
//                List<Output> temp = queryIssusByAuthorWhereAssignIsNull(rIssuses, authorName, module);
                // 创建了任务 但没给出去
//                if (!temp.isEmpty()) {
//                    outputsAssign.addAll(temp);
//                }
                outputsAuthorName.add(output);
            }
            output = new Output();
            output.setTitleOrName(module);
            output.setChild(outputsAuthorName);
            result.add(output);
        }
        return result;
    }

    /**
     * 排行
     *
     * @param identifier
     * @return
     * @throws RedmineException
     */
    public List<Output> queryRank(String identifier) throws RedmineException {
        getTeamleader(identifier);
        List<Output> dataList = query(identifier);
        List<Output> childOut = null, resultOutList = new ArrayList<>();
        for (String author : authorNames) {
            Output o = new Output();
            o.setTitleOrName(author);
            childOut = new ArrayList<>();
            for (Output module : dataList) {
                if (module.getChild() == null || module.getChild().isEmpty()) {
                    continue;
                }
                List<Output> temp = module.getChild().stream().filter(output -> {
                    return author.equals(output.getTitleOrName());
                }).collect(Collectors.toList());
                if (temp == null || temp.isEmpty())
                    continue;
                childOut.addAll(temp);
            }
            if (!childOut.isEmpty())
                o.setChild(childOut);
            resultOutList.add(o);
        }

        return resultOutList;
    }

    private void getTeamOutput(List<Output> outputs, String leaderName, List<Output> result) {
        if (outputs == null || outputs.isEmpty())
            return;
        for (Output output : outputs) {
            if (leaderName.equals(output.getTitleOrName())) {
//                output.setChild(null);
                result.add(output);
            }
            if (output.getChild() == null || output.getChild().isEmpty())
                continue;
            getTeamOutput(output.getChild(), leaderName, result);
        }
    }

//        initData();
//        RProject rp = queryByProjectName(identifier);
//        List<Output> result = new ArrayList<>();
//        Output output = null;
//        Set<String> teamLeader = getTeamleader(identifier);
//        List<RIssus> rIssuses = rp.getRIssuses();
//        if (rIssuses == null || rIssuses.isEmpty())
//            return result;
//        for (String leader : teamLeader) {
//            rIssuses = rIssuses.stream().filter(rIssus -> {
//                // 问题是给组长的 或是  组长创建的
//                return (leader.equals(rIssus.getAssigneeName()) || leader.equals(rIssus.getAuthorName()));
//            }).collect(Collectors.toList());
//            List<Output> tl = new ArrayList<>();
//            for (RIssus rIssus : rIssuses) {
//                output = new Output();
//                output.setTitleOrName(rIssus.getSubject());
//                output.setRatio(rIssus.getAssigneeDoneRatio());
//                tl.add(output);
//            }
//            output = new Output();
//            output.setTitleOrName(leader);
//            output.setChild(tl);
//            result.add(output);
//        }
//
//        return result;
//    }

    /**
     * 所有迭代版本
     *
     * @return
     * @throws RedmineException
     */
    public List<Project> queryAllProject() throws RedmineException {
        synchronized (Object.class) {
            return manager.getProjectManager().getProjects();
        }
    }

    public CycleDate getCycleDateByIdentifier(String identifier) {
//        initCycleDate();
        return getCacheCyc(CYCLE_DATA_MAP).get(identifier);
    }

    private Map<String, CycleDate> initCycleDate() {
        System.out.println(new SimpleDateFormat(DateUtil.FORMAT_DATETIME).format(new Date()) + " initCycleDate alled");

        Map<String, CycleDate> cycleDateMap = new HashMap<>();
        String startDate = null;
        for (int i = 1; i < 200; i++) {
            if (cycleDateMap.get(IDENTIFIER_PREFIX + (i - 1)) != null)
                startDate = cycleDateMap.get(IDENTIFIER_PREFIX + (i - 1)).getEndDate();
            else
                startDate = PROJECT_START_DATE_SPRINT1;
            String startDateTemp = DateUtil.format(DateUtil.addDateDay(DateUtil.parseDate(startDate), 1), DateUtil.FORMAT_DATE);
            String endDate = DateUtil.format(DateUtil.addDateDay(DateUtil.parseDate(startDate), 14), DateUtil.FORMAT_DATE);
            cycleDateMap.put(IDENTIFIER_PREFIX + i, new CycleDate(startDateTemp, endDate));
        }
        return cycleDateMap;
    }

    private static Set<String> authorNames = null;

    public Set<String> getTeamleader(String preIdentifier) throws RedmineException {
        if (authorNames != null)
            return authorNames;
        authorNames = new HashSet<>();
        MembershipManager membershipManager = manager.getMembershipManager();
        List<Membership> list = membershipManager.getMemberships(preIdentifier);
        for (Membership membership : list) {
            if (membership.getRoles().stream().filter(role -> {
                return "组长".equals(role.getName());
            }).count() > 0)
                authorNames.add(membership.getUserName());
        }
        return authorNames;
    }

    private List<Output> queryIssusByAssignName(List<RIssus> inputRIssuses, String assignName) {
        List<RIssus> rIssuses = inputRIssuses.stream().filter(rIssus -> {
            return assignName.equals(rIssus.getAssigneeName());
        }).collect(Collectors.toList());
        List<Output> outputsIssus = new ArrayList<>();
        for (RIssus rIssus : rIssuses) {
            Output output = new Output();
            output.setTitleOrName(rIssus.getSubject());
            output.setRatio(rIssus.getAssigneeDoneRatio());
            outputsIssus.add(output);
        }
        return outputsIssus;
    }

    private List<Output> queryIssusByAuthorWhereAssignIsNull(List<RIssus> inputRIssuses, String authorName, String module) {
        Output output = null;
        List<RIssus> rIssuses = inputRIssuses.stream().filter(rIssus -> {
            return (rIssus.getAssigneeName() == null || rIssus.getAssigneeName().equals(authorName)) && authorName.equals(rIssus.getAuthorName()) && module.equals(rIssus.getModule());
        }).collect(Collectors.toList());
        List<Output> outputsIssus = new ArrayList<>();
        for (RIssus rIssus : rIssuses) {
            output = new Output();
            output.setTitleOrName(rIssus.getSubject());
            output.setRatio(rIssus.getAssigneeDoneRatio());
            outputsIssus.add(output);
        }
        return outputsIssus;
    }


    public RProject queryByProjectName(String rProject) throws RedmineException {
        for (RProject project : getCache(ALL_PROJECTS_DATA)) {
            if (project.getIdentifier().equals(rProject))
                return project;
        }
        return new RProject();
    }


    private List<RProject> allIssusData() throws RedmineException {
        System.out.println(new SimpleDateFormat(DateUtil.FORMAT_DATETIME).format(new Date()) + " allIssusData alled");
        List<RProject> rProjects = new ArrayList<>();
        // 迭代   -   创建人 -
        RProject rProject = null;
        List<RIssus> rIssuses = null;
        RIssus rIssus = null;
        List<Project> projectList = queryAllProject();
//        List<Issue> issues2 = manager.getIssueManager().getIssues("sprint4", null);
//        for (Issue issue : issues2) {
//            try {
//                if ("陈 吉平".equals(issue.getAssigneeName()))
////                    System.out.println(1);
//                        System.out.println(issue.getParentId() + "_" + issue.getSubject());
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
        for (Project p : projectList) {
            if (!p.getName().startsWith("迭代"))
                continue;
            rProject = new RProject();
            rProject.setIdentifier(p.getIdentifier());
            rProject.setName(p.getName());
            List<Issue> issues = manager.getIssueManager().getIssues(p.getIdentifier(), null);
            rIssuses = new ArrayList<>();
            for (Issue issue : issues) {
                if (issue.getParentId() == null && issue.getTracker() != null && issue.getTracker().getId() == 1) {
                    rIssuses.add(RIssus.builder().authorName(issue.getAuthorName()).subject(issue.getSubject())
                            .assigneeName(issue.getAssigneeName()).assigneeDoneRatio(issue.getDoneRatio())
                            .id(issue.getId()).projectId(issue.getParentId()).authorId(issue.getAuthorId()).assigneeId(issue.getAssigneeId())
                            .module(getModule(issue.getCustomFieldByName("模块")))
                            .build());
                }
            }
            rProject.setRIssuses(rIssuses);
            rProjects.add(rProject);
        }
        return rProjects;
    }

    private String getModule(CustomField customField) {
        if (customField == null)
            return null;
        moduleNames.add(customField.getValue());
        return customField.getValue();
    }

    public static List<String> getAuthorNames(List<RIssus> rIssuses) {
        Set<RIssus> set = new TreeSet<RIssus>((issue1, issue2) -> issue1.getAuthorName().compareTo(issue2.getAuthorName()));
        set.addAll(rIssuses);
        return new ArrayList<>(set.stream().map(RIssus::getAuthorName).collect(Collectors.toList()));
    }

    public static Set<String> getAssignNames(List<RIssus> rIssuses, String authorName, Set<String> teamLeader) {
        return rIssuses.stream().filter(rIssus -> {
            return authorName.equals(rIssus.getAuthorName()) || authorName.equals(rIssus.getAssigneeName());
        }).map(RIssus::getAssigneeName).collect(Collectors.toSet());
    }

    private List<RProject> getCache(String key) {
        try {
            if (cache.getIfPresent(key) == null)
                cache.put(ALL_PROJECTS_DATA, this.allIssusData());
            return cache.getIfPresent(key);
        } catch (Exception e) {
            try {
                cache.put(ALL_PROJECTS_DATA, this.allIssusData());
            } catch (RedmineException e1) {
                e1.printStackTrace();
            }

        }
        return cache.getIfPresent(key);
    }

    private Map<String, CycleDate> getCacheCyc(String key) {
        try {
            if (cacheCyc.getIfPresent(key) == null)
                cacheCyc.put(CYCLE_DATA_MAP, initCycleDate());
            return cacheCyc.getIfPresent(key);
        } catch (Exception e) {
            cacheCyc.put(CYCLE_DATA_MAP, initCycleDate());
        }
        return cacheCyc.getIfPresent(key);
    }

}
